package cn.tedu.java.thread;

import java.util.ArrayList;

/**
 * 手写阻塞队列
 * 1)数据的存储结构: 数组
 * 2)数据的存取算法：FIFO(先进先出)
 * 3)线程策略：要保证存取数据的线程安全，同时队列满了要阻塞，队列空了也要阻塞。
 */
class SimpleArrayBlockingQueue<T>{//这里的T为泛型
    /**基于数组存储元素*/
    private Object[] array;
    /**存储的有效元素的个数*/
    private int size;

    public SimpleArrayBlockingQueue(int capacity){
        this.array=new Object[capacity];//[null,null,null,...]
    }

    /**向队列的size位置放数据，满了线程则阻塞，同时要通知消费者线程取数据*/
    public synchronized void put(T t)throws InterruptedException{
       //1.检测队列是否满了，满了则让当前线程阻塞，并释放锁
        while(size==array.length){this.wait();}//wait方法会释放锁
       //2.将数据存储到队列的size位置
        array[size]=t;
       //3.修改队列中有效元素的个数
        size++;
       //4.唤醒消费者线程(在同一个对象锁上处于阻塞状态的线程)
        this.notifyAll();//不会释放锁，直到程序结束
    }
    /**从队列下标为0的位置取数据，空了则等待，通知通知生产者线程继续放*/
    public synchronized T take() throws InterruptedException {
        //1.检测容器是否为空，假如为空，则让取数据的线程等待
        while(size==0){this.wait();}
        //2.取下标为0位置的数据
        T t=(T)array[0];
        //3.移动元素
        System.arraycopy(array, 1,array,0,size-1);
        //4.修改有效元素个数。
        size--;
        //5.size位置元素设置为null(初始状态)
        array[size]=null;
        //6.通知生产者线程继续放数据
        this.notifyAll();
        return t;
    }
}

public class SimpleArrayBlockingQueueTests {
    public static void main(String[] args) {
        //1.创建阻塞队列对象
        SimpleArrayBlockingQueue<Integer> queue=
                new SimpleArrayBlockingQueue<>(3);
        //2.创建生产者线程，此线程不断向容器放数据
        Thread producer=new Thread(new Runnable() {
            @Override
            public void run() {
                int i=1;
                while(true){
                    try {
                        queue.put(i);
                        i++;
                        Thread.sleep(500);
                    }catch (InterruptedException e){e.printStackTrace();}
                }
            }
        });
        //3.创建消费者线程，此线程不断从容器取数据
        Thread consumer=new Thread(new Runnable(){
            @Override
            public void run() {
                while(true){
                    try {
                        System.out.println(queue.take());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        //4.启动生产者和消费者线程。
        producer.start();
        consumer.start();
    }
}
